#ifndef UTILS_H
#define UTILS_H


// Utility class for debugging deduced types
template <typename T>
class error;


// Utility meta-function for detecting type validity
template <typename ...T>
using void_t = void;

// If you are using an older compiler,
// replace the above definition with the following:
//     template<typename... Ts> struct make_void { typedef void type;};
//     template<typename... Ts> using void_t = typename make_void<Ts...>::type;


// Meta-function that returns the type of an element
// in an iterable collection
//std::declval的功能：返回某个类型T的右值引用，不管该类型是否有默认构造函数或者该类型是否可以创建对象,返回某个类型T的右值引用，
//这个动作是在编译时完成的，所以很多人把std::declval也称为编译时工具
template <typename T>
using contained_type =
    std::remove_cv_t<
        std::remove_reference_t<
            decltype(*std::begin(std::declval<T>()))
        >
    >;


// Meta-function that returns a type with references stripped
template <typename T>
struct remove_reference {
    using type = T;
};

template <typename T>
struct remove_reference<T&> {
    using type = T;
};

template <typename T>
struct remove_reference<T&&> {
    using type = T;
};

template <typename T>
using remove_reference_t = typename remove_reference<T>::type;


// Meta-function that detects whether a type
// has a nested `value_type` type definition
template < typename C
         , typename = void_t<>
         >
struct has_value_type : std::false_type {};

template<typename C>
struct has_value_type<C, void_t<typename C::value_type>> : std::true_type {};


template < typename C
         , typename = void_t<>
         >
struct is_iterable : std::false_type {};
// 但是对于成员函数的判断，就不能直接使用 `decltype(T::Func)`，
//因为这只适用于静态成员函数的情况，对于非静态的成员函数来说，
//T::Func 是一个非法的表达式。假设 a 是 T 类型的一个对象实例，
//则 `a.T::Func` 或 `a.func` 才是合法的表达式。所以对于成员函数来说，
//如果还是使用 `decltype(T::Func)`的语法形式，则无论的一项是否有名为 Func 的成员函数，
//都会因为表达式非法而替换失败，从而匹配成 false_type 的结果。
//难道还要构造一个 T 类型的对象才行吗？当然不是，C++ 不是还有`std::declval()`嘛。
//当 std::void_t<> 替换失败时，就会触发 SFINAE 机制删除相应的模板参数替换得到的错误结果。
//`std::declval() `的作用是返回任意类型 T 的右值引用类型 T&& ，
//可以借助这个右值引用调用 T 的成员函数，最终的效果就是在没有构造 T 的任何实例的情况下调用了 T 的成员函数，当然，这一切都是在编译期间完成的，编译器甚至都不需要函数的完整定义。
//具体的做法就是用`std::declval() `得到对象的右值引用，然后使用这个右值引用调用成员函数，
//再用`decltype()`推导函数调用返回值的类型：
//std::begin是否支持迭代
template <typename C>
struct is_iterable
    <C, void_t < decltype(*std::begin(std::declval<C>()))
               , decltype(std::end(std::declval<C>()))
               >>
    : std::true_type {};


// Meta function that always returns false
// Useful with static_assert and if-constexpr
template <typename...>
struct false_: std::false_type {};





#endif /* !UTILS_H */
